﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Diagnostics;

namespace Tencent.OA.App
{
    /// <summary>
    /// 定义一个用于在WCF服务主机和客户端之间传递已分页数据的实体。
    /// </summary>
    [DataContract]
    public class PagerEntity<T> : IPager<T>
    {
        /// <summary>
        /// 从一个 IList&lt;T&gt; 类型的数据源 <paramref name="source"/> 创建 PagerEntity&lt;T&gt; 的新实例，
        /// 数据源 <paramref name="source"/> 在该实例对象中不再进行分页处理，而是直接传递。
        /// </summary>
        /// <remarks>
        /// 数据源对象应该是一个在数据库查询时已进行分页处理的数据列表。
        /// </remarks>
        /// <param name="source">一个 IList&lt;T&gt; 类型的数据源。</param>
        /// <param name="pageIndex">数据源中从0开始的索引页。</param>
        /// <param name="pageSize">数据源中已分页的数据大小。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="source"/> 不能为null。</exception>
        public PagerEntity(IList<T> source, int pageIndex, int pageSize)
            : this(source, pageIndex, pageSize, source.Count())
        {
        }

        /// <summary>
        /// 从一个 IList&lt;T&gt; 类型的数据源 <paramref name="source"/> 创建 PagerEntity&lt;T&gt; 的新实例，
        /// 数据源 <paramref name="source"/> 在该实例对象中不再进行分页处理，而是直接传递。
        /// </summary>
        /// <remarks>
        /// 数据源对象应该是一个在数据库查询时已进行分页处理的数据列表。
        /// </remarks>
        /// <param name="source">一个 IList&lt;T&gt; 类型的数据源。</param>
        /// <param name="pageIndex">数据源中从0开始的索引页。</param>
        /// <param name="pageSize">数据源中已分页的数据大小。</param>
        /// <param name="totalItemCount">进行数据源分页时，已计算的数据库表总数据行数。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="source"/> 不能为null。</exception>
        public PagerEntity(IList<T> source, int pageIndex, int pageSize, int totalItemCount)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            InitializeProperty(pageIndex, pageSize, totalItemCount);
            this.PagedList = source;
        }

        /// <summary>
        /// 从一个 IQueryable&lt;T&gt; 类型的数据源 <paramref name="source"/> 创建 PagerEntity&lt;T&gt; 的新实例，
        /// 该数据源是一个Linq查询对象，将在新实例对象中进行分页处理，计算总页数，并执行对数据库表的查询。
        /// </summary>
        /// <param name="source">一个 IQueryable&lt;T&gt; 类型的数据源。</param>
        /// <param name="pageIndex">从数据源 <paramref name="source"/> 中进行分页处理时的从0开始的索引页。</param>
        /// <param name="pageSize">从数据源 <paramref name="source"/> 中进行分页处理时每页的数据行数。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="source"/> 不能为null。</exception>
        public PagerEntity(IQueryable<T> source, int pageIndex, int pageSize)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            int totalItemCount = source.Count();
            InitializeProperty(pageIndex, pageSize, totalItemCount);
            int index = pageIndex;

            // 如果超出数据源页数，则始终返回最后一页
            //
            if (CurrentPageIndexOverflowPageCount)
                index = PageCount - 1;
            this.PagedList = (source.Skip<T>(index * pageSize).Take<T>(pageSize).ToList());
        }

        /// <summary>
        /// 从数据源获取的经过分页的数据。
        /// </summary>
        [DataMember]
        public IList<T> PagedList { get; set; }

        /// <summary>
        /// 初始化页索引、每页数据行数、数据源总行数大小。
        /// </summary>
        /// <param name="index"></param>
        /// <param name="pageSize"></param>
        /// <param name="totalCount"></param>
        protected virtual void InitializeProperty(int index, int pageSize, int totalCount)
        {
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException("PageIndex cannot be below 0.");
            }
            if (pageSize < 1)
            {
                throw new ArgumentOutOfRangeException("PageSize cannot be less than 1.");
            }

            TotalItemCount = totalCount;
            PageSize = pageSize;
            PageIndex = index;
        }

        /// <summary>
        /// 获取用于分页的数据源总记录数。
        /// </summary>
        [DataMember]
        public int TotalItemCount { get; set; }

        /// <summary>
        /// 获取分页的当前显示页的索引。
        /// </summary>
        [DataMember]
        public int PageIndex { get; set; }

        /// <summary>
        /// 获取当前分页的每页数据行数大小。
        /// </summary>
        [DataMember]
        public int PageSize { get; set; }

        /// <summary>
        /// 获取数据源分页后的总页数。
        /// </summary>
        public int PageCount
        {
            get
            {
                if (TotalItemCount > 0)
                {
                    return (int)Math.Ceiling(TotalItemCount / (double)PageSize);
                }
                return 0;
            }
        }

        /// <summary>
        /// 获取当前页的页码。
        /// 如果PageIndex超出数据源的实际分页数则返回PageCount，否则为PageIndex + 1。
        /// </summary>
        public int PageNumber
        {
            get
            {
                if (TotalItemCount == 0)
                    return 0;

                if (this.CurrentPageIndexOverflowPageCount)
                    return PageCount;
                return this.PageIndex + 1;
            }
        }

        /// <summary>
        /// 获取指示当前分页是否有上一页。
        /// </summary>
        public bool HasPreviousPage { get { return (PageIndex > 0); } }

        /// <summary>
        /// 获取指示当前分页是否有下一页。
        /// </summary>
        public bool HasNextPage { get { return PageIndex < (PageCount - 1); } }

        /// <summary>
        /// 获取指示当前分页是否是第一页。
        /// </summary>
        public bool IsFirstPage { get { return PageIndex <= 0; } }

        /// <summary>
        /// 获取指示当前分页项是否是最后一页。
        /// </summary>
        public bool IsLastPage { get { return PageIndex >= (PageCount - 1); } }

        /// <summary>
        /// 获取一个值，该值指示指定的页索引是否超出数据源分页的最大页数。
        /// </summary>
        public bool CurrentPageIndexOverflowPageCount { get { return (PageCount > 0 && PageIndex >= PageCount); } }
    }

    /// <summary>
    /// 提供一个PagerEntity类的扩展，以进行分页处理。
    /// </summary>
    public static class PagerEntityExtensions
    {
        /// <summary>
        /// 从一个 IQueryable&lt;T&gt; 类型的数据源 <paramref name="source"/> 创建 PagerEntity&lt;T&gt; 的新实例，
        /// 该数据源是一个Linq查询对象，将在新实例对象中进行分页处理，计算总页数，并执行对数据库表的查询。
        /// </summary>
        public static PagerEntity<T> ToPagerEntity<T>(this IQueryable<T> source, int pageIndex, int pageSize)
        {
            return new PagerEntity<T>(source, pageIndex, pageSize);
        }

        /// <summary>
        /// 从一个 IList&lt;T&gt; 类型的数据源 <paramref name="source"/> 创建 PagerEntity&lt;T&gt; 的新实例，
        /// 数据源 <paramref name="source"/> 在该实例对象中不再进行分页处理，而是直接传递。
        public static PagerEntity<T> ToPagedEntity<T>(this IList<T> source, int pageIndex, int pageSize)
        {
            return ToPagedEntity(source, pageIndex, pageSize, source.Count());
        }

        /// <summary>
        /// 从一个 IList&lt;T&gt; 类型的数据源 <paramref name="source"/> 创建 PagerEntity&lt;T&gt; 的新实例，
        /// 数据源 <paramref name="source"/> 在该实例对象中不再进行分页处理，而是直接传递。
        public static PagerEntity<T> ToPagedEntity<T>(this IList<T> source, int pageIndex, int pageSize, int totalItemCount)
        {
            return new PagerEntity<T>(source, pageIndex, pageSize, totalItemCount);
        }
    }
}
